﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DataProcessor.Core;

namespace DataProcessor.FashionData
{
    public class MediaevalEvaluator
    {
        IList<FlickrImage> _groundTruth;
        string _finalGtFile;
        string _gtMv;
        string _gtFinal;

        public static void Run()
        {
            //string testFile = @"D:\Data\Datasets\Fashion10000\Fashion10000_Test20.csv";
            //string annotationFile = @"D:\Data\Dropbox\University\Fashion10000\HIT Result\Processed\Annotation_PerImage_All.csv";
            //string gtFile = @"E:\Data\Datasets\Fashion10000\Test20_Gt1.csv";
            string gtFile = @"F:\Data\Dropbox\University\Mediaeval 2013\Submissions\GT\Test20_GT.csv";
            
            //CreateGtFile(testFile, annotationFile, gtFile);
            //GetExcept(@"D:\Data\Dropbox\University\Mediaeval 2013\Sumissions\TUD\me13cs_TUD2013_run1WorkerOnly.csv",
            //    @"D:\Data\Dropbox\University\Mediaeval 2013\Sumissions\LIA\me13cs_LIA_ALL.txt");
            var me = new MediaevalEvaluator(gtFile);
            //me.CreateFinelGtFile(gt2File);

            // Currently the file 'NewEval.csv' is the valid evaluation result since it is based on the swaped columns (which is the right GT file)
           // me.EvaluateRuns(@"D:\Data\Dropbox\University\UMAP 2014\New Runs\Mike", @"D:\Data\Dropbox\University\UMAP 2014\New Runs\EvalMike.csv");

            me.EvaluateRuns(@"F:\Data\Dropbox\University\Mediaeval 2013\Submissions\Babak", @"F:\Data\Dropbox\University\UMAP 2014\New Runs\EvalBabak.csv");
            
            //me.PrintConfusionMatrix(gtFile, @"D:\Data\Dropbox\University\Mediaeval 2013\Submissions\GT\Test20_mv.csv");
            
            //me.CreateRun(@"E:\Dropbox\University\Fashion10000\HIT Result\Processed\Annotation_PerImage_All.csv",
            //    @"E:\Dropbox\University\Mediaeval 2013\Sumissions\Fashion10000_Test20.csv", 
            //    @"E:\Dropbox\University\Mediaeval 2013\Sumissions\Babak\Results_mv.csv");

            //me.CreateMajorClassRun(@"D:\Data\Dropbox\University\Mediaeval 2013\Submissions\Fashion10000_Test20.csv",
            //    @"D:\Data\Dropbox\University\Mediaeval 2013\Submissions\Babak\Baseline_MajorClass.csv");

            //me.compareMv(@"E:\Dropbox\University\Mediaeval 2013\Sumissions\Babak\Results_mv.csv",
            //     @"E:\Dropbox\University\Mediaeval 2013\Sumissions\UTexas2\me13cs_UTexas2013_mv.csv",
            //    @"E:\Dropbox\University\Mediaeval 2013\Sumissions\mv_comparision.csv");
        }

        public MediaevalEvaluator(string groudTruthPath)
        {
            _groundTruth = File.ReadAllLines(groudTruthPath).Select(l => new FlickrImage(l)).ToList();
            //_finalGtFile = @"D:\Data\Dropbox\University\Fashion10000\HIT Result\Luke\Test20_Gt_Final.csv";
            
            // This is generated based on majority voting
            _gtMv = @"F:\Data\Dropbox\University\Mediaeval 2013\Submissions\GT\Test20_mv.csv";
            
            // This is file with swaped column of the file sent by Luke (i.e. Test20_Luke.csv)
            // The file sent by Luke has wrong column order
            // This is the right file
            _gtFinal = @"F:\Data\Dropbox\University\Mediaeval 2013\Submissions\GT\Test20_GT.csv";
        }

        public void CreateRun(string allAnnotaionsFile, string imageUrls, string outputFile)
        {
            var output = File.ReadAllLines(allAnnotaionsFile).ToCsvDictionary()
                .Select(i => new { Url = i["PictureURL"], Label1 = i["Majority Q1"], Label2 = i["Majority Q2"] })
                .Join(File.ReadAllLines(imageUrls).Select(l => l.Trim()) , a => a.Url, u => u, (a, u) => a)
                .Select(a => string.Format("{0},{1},{2}", a.Url, a.Label1, a.Label2));

            var header = new string[] { "ImageUrl,Label1,Label2" };

            File.WriteAllLines(outputFile, header.Concat(output));
        }

        public void CreateMajorClassRun(string imageUrls, string outputFile)
        {
            var output = File.ReadAllLines(imageUrls).Select(i => string.Format("{0},yes,no", i.Trim()));

            var header = new string[] { "ImageUrl,Label1,Label2" };

            File.WriteAllLines(outputFile, header.Concat(output));
        }

        public void compareMv(string myMv, string otherMv, string outputFile)
        {
            var my = File.ReadAllLines(myMv).Select(l => new FlickrImage(l)).ToList();
            var other = File.ReadAllLines(otherMv).Select(l => new FlickrImage(l)).ToList();

            var join = other.Join(my, gt => gt.Url, g2 => g2.Url, (gt, g2) => new
            {
                Url = gt.Url,
                Label1 = gt.Label1,
                Label2 = gt.Label2,
                Label1_2 = g2.Label1,
                Label2_2 = g2.Label2
            }).ToList();

            var output = join.Where(j => j.Label1.ToLower() != j.Label1_2.ToLower() || j.Label2.ToLower() != j.Label2_2.ToLower())
                .Select(j => string.Format("{0},{1},{2},{3},{4}", j.Url, j.Label1, j.Label1_2, j.Label2, j.Label2_2));

            var header = new string[] { "ImageUrl,Other1,My1,Other2,My2" };

            File.WriteAllLines(outputFile, header.Concat(output));

        }

        public static void CreateGtFile(string testFile, string annotaionFile, string outputFile)
        {
            var testUrls = File.ReadAllLines(testFile).Select(l => l.Trim()).ToList();
            
            var annotations = File.ReadAllLines(annotaionFile).ToCsvDictionary()
                .Select(i => new FlickrImage { Url = i["PictureURL"], Label1 = i["Majority Q1"], Label2 = i["Majority Q2"] })
                .ToList();

            var nonExistingUrls = testUrls.Except(annotations.Select(a => a.Url)).ToList();
            File.WriteAllLines(outputFile + ".rest", nonExistingUrls);
            
            var output = annotations.Join(testUrls, o => o.Url, i => i, (o, i) => o)
                .Select(fi => string.Format("{0},{1},{2}", fi.Url, fi.Label1, fi.Label2))
                .ToList();

            var header = new string[] { "ImageUrl,Label1,Label2" };
            File.WriteAllLines(outputFile, header.Concat(output));
        }


        public void CreateFinelGtFile(string gt2FilePath)
        {
            var gt2 = File.ReadAllLines(gt2FilePath).Select(l => new FlickrImage(l)).ToList();

            var join = gt2.Join(_groundTruth, gt => gt.Url, g2 => g2.Url, (gt, g2) => new 
            {
                Url = gt.Url,
                Label1 = gt.Label1,
                Label2 = gt.Label2,
                Label1_2 = g2.Label1,
                Label2_2 = g2.Label2
            }).ToList();

            var strongGt = join.Where(j => j.Label1 == j.Label1_2 && j.Label2 == j.Label2_2);


            var output = strongGt.Select(gt => String.Format("{0},{1},{2}", gt.Url, gt.Label1, gt.Label2));
            var header = new string[] { "ImageUrl,Label1,Label2" };
            File.WriteAllLines(_finalGtFile, header.Concat(output));
        }

        public Tuple<double, double> GetF1Score(string submissionPath, string groudTruthPath)
        {
            var groundTruth = File.ReadAllLines(groudTruthPath).Select(l => new FlickrImage(l)).ToList();
            var labels = File.ReadAllLines(submissionPath).Select(l => new FlickrImage(l));
            labels = labels.Where(l => !String.Equals(l.Url.ToLower(), "imageurl")).ToList();

            var join = labels.Join(groundTruth, l => l.Url, gt => gt.Url, (l, gt) => new 
            { 
                Url = l.Url,
                Label1 = l.Label1,
                Label2 = l.Label2,
                GtLabel1 = gt.Label1,
                GtLabel2 = gt.Label2
            });

            Func<string, string, int> GetCounts1 = (predicted, actual) => 
                join.Where(j => String.Equals(j.Label1, predicted) && String.Equals(j.GtLabel1, actual)).Count();

            Func<string, string, int> GetCounts2 = (predicted, actual) =>
                join.Where(j => String.Equals(j.Label2, predicted) && String.Equals(j.GtLabel2, actual)).Count();

            int tp1 = GetCounts1("yes", "yes");
            int tp2 = GetCounts2("yes", "yes");

            int fp1 = GetCounts1("yes", "no");
            int fp2 = GetCounts2("yes", "no");

            int fn1 = GetCounts1("no", "yes");
            int fn2 = GetCounts2("no", "yes");

            double prec1 = (double)tp1 / (tp1 + fp1);
            double prec2 = (double)tp2 / (tp2 + fp2);

            double recal1 = (double)tp1 / (tp1 + fn1);
            double recal2 = (double)tp2 / (tp2 + fn2);

            double f1_1 = 2 * prec1 * recal1 / (prec1 + recal1);
            double f1_2 = 2 * prec2 * recal2 / (prec2 + recal2);

            return new Tuple<double, double>(f1_1, f1_2);
        }

        public void EvaluateRuns(string submissionFolder, string outputFile)
        {
            int numSamples = _groundTruth.Where(fi =>
                !string.Equals(fi.Label1, "notsure") &&
                !string.Equals(fi.Label2, "notsure") &&
                !string.Equals(fi.Label1, "n/a") &&
                !string.Equals(fi.Label2, "n/a")).Count();

            Console.WriteLine("Total number of samples in GT file: " + numSamples);
            
            var output = Directory.GetDirectories(submissionFolder).SelectMany(d => 
            {
                return Directory.GetFiles(d).Select(f => 
                {
                    var mvF1Score = GetF1Score(f, _gtMv);
                    var gtF1Score = GetF1Score(f, _gtFinal);

                    return string.Format("{0},{1},{2},{3},{4},{5},{6}", 
                        d.Substring(d.LastIndexOf('\\') + 1),
                        f.Substring(f.LastIndexOf('\\') + 1),
                        File.ReadAllLines(f).Count() - 1,
                        mvF1Score.Item1,
                        mvF1Score.Item2,
                        gtF1Score.Item1,
                        gtF1Score.Item2);
                });
            });

            var header = new string[] { "Team,Submission,NumSamples,MV_F1Label1,MV_F1Label2,F1Label1,F1Label2" };
            File.WriteAllLines(outputFile, header.Concat(output));
        }

        public static void GetExcept(string file1, string file2)
        {
            var urls1 = File.ReadAllLines(file1).Select(l => new FlickrImage(l)).Select(fi => fi.Url);
            var urls2 = File.ReadAllLines(file2).Select(l => new FlickrImage(l)).Select(fi => fi.Url);

            urls2.Except(urls1).ToList().ForEach(Console.WriteLine);
        }

        public void PrintConfusionMatrix(string groudTruthPath, string submissionPath)
        {
            var groundTruth = File.ReadAllLines(groudTruthPath).Select(l => new FlickrImage(l)).ToList();
            var labels = File.ReadAllLines(submissionPath).Select(l => new FlickrImage(l));
            labels = labels.Where(l => !String.Equals(l.Url.ToLower(), "imageurl")).ToList();

            var join = labels.Join(groundTruth, l => l.Url, gt => gt.Url, (l, gt) => new
            {
                Url = l.Url,
                Label1 = l.Label1,
                Label2 = l.Label2,
                GtLabel1 = gt.Label1,
                GtLabel2 = gt.Label2
            });

            var classes = new string[] { "yes", "no" };

            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    Console.Write(join.Where(im => im.Label2 == classes[i] && im.GtLabel2 == classes[j]).Count() + "\t");
                }
                Console.WriteLine();
            }
        }

    }

    public class FlickrImage
    {
        public FlickrImage()
        { 
        }
        
        public FlickrImage(string csvLine)
        {
            string[] tokens = csvLine.Split(',');

            Url = tokens[0];
            Label1 = tokens[1].ToLower();
            Label2 = tokens[2].ToLower();
        }
        
        public string Url { get; set; }

        // Fashion non-fashion label
        public string Label1 { get; set; }

        // Tag confirmation label
        public string Label2 { get; set; }

    }
}
